<!doctype html>



  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Android," />





  <link rel="alternate" href="/atom.xml" title="结网之路" type="application/atom+xml" />




  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />






<meta name="description" content="本章主要讲了如何使用 intent 拍照，存储照片和展示照片
GitHub 地址：完成16章，未完成挑战完成16章挑战1完成16章挑战2

1. 外部存储相机照片动辄几 MB 大小，直接保存在数据库中肯定不现实。很自然，大家会想到直接使用设备的文件系统。一般来讲，应用都应该使用私有存储空间保存各类文件。还记得吗?在前面章节中，我们在私有存储空间保存过 SQLite 数据文件。使用类似 Conte">
<meta property="og:type" content="article">
<meta property="og:title" content="Android编程权威指南（第二版）学习笔记（十六）—— 第16章 使用 intent 拍照">
<meta property="og:url" content="https://kniost.github.io/2017-01-12-cj0ag5y69000yifs6oc1j0hco/index.html">
<meta property="og:site_name" content="结网之路">
<meta property="og:description" content="本章主要讲了如何使用 intent 拍照，存储照片和展示照片
GitHub 地址：完成16章，未完成挑战完成16章挑战1完成16章挑战2

1. 外部存储相机照片动辄几 MB 大小，直接保存在数据库中肯定不现实。很自然，大家会想到直接使用设备的文件系统。一般来讲，应用都应该使用私有存储空间保存各类文件。还记得吗?在前面章节中，我们在私有存储空间保存过 SQLite 数据文件。使用类似 Conte">
<meta property="og:updated_time" content="2017-03-14T06:46:46.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android编程权威指南（第二版）学习笔记（十六）—— 第16章 使用 intent 拍照">
<meta name="twitter:description" content="本章主要讲了如何使用 intent 拍照，存储照片和展示照片
GitHub 地址：完成16章，未完成挑战完成16章挑战1完成16章挑战2

1. 外部存储相机照片动辄几 MB 大小，直接保存在数据库中肯定不现实。很自然，大家会想到直接使用设备的文件系统。一般来讲，应用都应该使用私有存储空间保存各类文件。还记得吗?在前面章节中，我们在私有存储空间保存过 SQLite 数据文件。使用类似 Conte">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    sidebar: {"position":"right","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://kniost.github.io/2017-01-12-cj0ag5y69000yifs6oc1j0hco/"/>





  <title> Android编程权威指南（第二版）学习笔记（十六）—— 第16章 使用 intent 拍照 | 结网之路 </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  














  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">结网之路</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">临渊羡鱼，不如退而结网</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <link itemprop="mainEntityOfPage" href="https://kniost.github.io/2017-01-12-cj0ag5y69000yifs6oc1j0hco/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="kniost">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="结网之路">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                Android编程权威指南（第二版）学习笔记（十六）—— 第16章 使用 intent 拍照
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-01-12T17:55:10+08:00">
                2017-01-12
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android编程权威指南学习笔记/" itemprop="url" rel="index">
                    <span itemprop="name">Android编程权威指南学习笔记</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2017-01-12-cj0ag5y69000yifs6oc1j0hco/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017-01-12-cj0ag5y69000yifs6oc1j0hco/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          

          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <blockquote>
<p>本章主要讲了如何使用 intent 拍照，存储照片和展示照片</p>
<p>GitHub 地址：<br><a href="https://github.com/kniost/Android-Programming-Big-Nerd-Ranch-Guide-2nd/commit/a3abc7009d2c225159c71e6d71e351eb6284bdfb" target="_blank" rel="external">完成16章，未完成挑战</a><br><a href="https://github.com/kniost/Android-Programming-Big-Nerd-Ranch-Guide-2nd/commit/acaa89b7be9f3b7f481a5e4c190bfaf224c3e3ee" target="_blank" rel="external">完成16章挑战1</a><br><a href="https://github.com/kniost/Android-Programming-Big-Nerd-Ranch-Guide-2nd/commit/2462577c3c4b4acb614b6e01a69124d235028cef" target="_blank" rel="external">完成16章挑战2</a></p>
</blockquote>
<h1 id="1-外部存储"><a href="#1-外部存储" class="headerlink" title="1. 外部存储"></a>1. 外部存储</h1><p>相机照片动辄几 MB 大小，直接保存在数据库中肯定不现实。很自然，大家会想到直接使用设备的文件系统。<br>一般来讲，应用都应该使用私有存储空间保存各类文件。还记得吗?在前面章节中，我们在私有存储空间保存过 SQLite 数据文件。使用类似 Context.getFileStreamPath(String)和 Context.getFilesDir()这样的方法，我们也可以实现这样的存储目标，下表所示：</p>
<table>
<thead>
<tr>
<th style="text-align:left">Context 类提供的方法</th>
<th style="text-align:left">使用目的</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>File getFilesDir()</code></td>
<td style="text-align:left">获取/data/data/<packagename>/files 目录</packagename></td>
</tr>
<tr>
<td style="text-align:left"><code>FileInputStream openFileInput(String name)</code></td>
<td style="text-align:left">打开现有文件进行读取</td>
</tr>
<tr>
<td style="text-align:left"><code>FileOutputStream openFileOutput(String name, int mode)</code></td>
<td style="text-align:left">打开文件进行写入，如不存在就创建它</td>
</tr>
<tr>
<td style="text-align:left"><code>File getDir(String name, int mode)</code></td>
<td style="text-align:left">获取/data/data/<packagename>/目录的子目录(如不存在就先创建它)</packagename></td>
</tr>
<tr>
<td style="text-align:left"><code>String[] fileList()</code></td>
<td style="text-align:left">获取/data/data/<packagename>/files 目录下的文件列表。可与其他方法配合使用，例如 openFileInput(String)</packagename></td>
</tr>
<tr>
<td style="text-align:left"><code>File getCacheDir()</code></td>
<td style="text-align:left">获取/data/data/<packagename>/cache 目录。应注意及时清理该目录，并节约使用空间</packagename></td>
</tr>
</tbody>
</table>
<p>如果想存储的文件仅供应用内部使用，使用上表中的各类方法就可以了。而如果想共享文件给其他应用或是接收其他应用的文件(如相机应用拍摄的照片)时，路只有一条:使用外部存储保存文件。<br>外部存储有两类:主外部存储和其他各类存储介质。所有的 Android 设备至少应有一个主外部存储地。使用<code>Environment.getExternalStorageDirectory()</code>可以返回这个外部存储目录。 以前，这个存储地通常是指 SD 卡，但现在都已基本整合至了设备内部。即使现在还有设备使用扩展外部存储，也应算作其他各类存储介质这一类了。<br>Context 也提供了一些访问外部存储空间要用到的方法，如下表所示。</p>
<table>
<thead>
<tr>
<th style="text-align:left">方法</th>
<th style="text-align:left">使用目的</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>File getExternalCacheDir()</code></td>
<td style="text-align:left">获取主外部存储上的缓存文件目录。用法类似 getCacheDir()方法，但要注意，Android 一般不会自动清理该目录</td>
</tr>
<tr>
<td style="text-align:left"><code>File[] getExternalCacheDirs()</code></td>
<td style="text-align:left">获取多个外部存储上的缓存文件目录</td>
</tr>
<tr>
<td style="text-align:left"><code>File getExternalFilesDir(String)</code></td>
<td style="text-align:left">获取主外部存储上存放常规文件的文件目录。通过 String 参数，可访问特定内容类型的子目录。内容类型常量以 DIRECTORY<em>为前缀，定义在 Environment 中 。 例如 , 用于 图像 文件 的 Environment.DIRECTORY</em> PICTURES</td>
</tr>
<tr>
<td style="text-align:left"><code>File[] getExternalFilesDirs(String)</code></td>
<td style="text-align:left">类似 getExternalFilesDir(String)方法，但该方法可获取指定类型的所有文件目录</td>
</tr>
<tr>
<td style="text-align:left"><code>File[] getExternalMediaDirs()</code></td>
<td style="text-align:left">获取 Android 存储图片、视频和音乐文件的所有外部文件目录。和 getExternalFilesDir(Environment.DIRECTORY_PICTURES) 方法 区别 在于，调用该方法，多媒体扫描器会自动扫描目标目录，并将存放的多媒体文件暴露给能够播放音乐、浏览视频和图片的应用。也就是说， getExternalMediaDirs()方法返回目录中存放的任何文件都会自动出现在多媒体应用中</td>
</tr>
</tbody>
</table>
<h2 id="1-1-指定照片存放位置"><a href="#1-1-指定照片存放位置" class="headerlink" title="1.1 指定照片存放位置"></a>1.1 指定照片存放位置</h2><p>首先，一张照片的文件名我们用一个 Crime 的 ID 来标识，所以在 Crime.java 中加入了获取文件名的方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">getPhotoFileName</span><span class="params">()</span> </span>&#123;</div><div class="line">	<span class="keyword">return</span> <span class="string">"IMG_"</span> + getId().toString() + <span class="string">".jpg"</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后在 CrimeLab.java 中加入获取路径文件的函数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> File <span class="title">getPhotoFile</span><span class="params">(Crime crime)</span> </span>&#123;</div><div class="line">    File externalFilesDir = mContext</div><div class="line">            .getExternalFilesDir(Environment.DIRECTORY_PICTURES);</div><div class="line"></div><div class="line">    <span class="keyword">if</span> (externalFilesDir == <span class="keyword">null</span>) &#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">return</span> <span class="keyword">new</span> File(externalFilesDir, crime.getPhotoFileName());</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="1-2-外部存储使用权限"><a href="#1-2-外部存储使用权限" class="headerlink" title="1.2 外部存储使用权限"></a>1.2 外部存储使用权限</h2><p>读写外部存储需要获得权限，一般在<code>AndroidManifest.xml</code>中使用<code>&lt;uses-permission&gt;</code>标签来使用。而对于 API 19（Android 4.4）及以后的新版系统来说，应用不需要再申请 <code>Context.getExternalFilesDir(String)</code> 所需要的权限了，所以这个权限申请是这么写的：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">uses-permission</span></span></div><div class="line">    <span class="attr">android:name</span>=<span class="string">"android.permission.READ_EXTERNAL_STORAGE"</span></div><div class="line">    <span class="attr">android:maxSdkVersion</span>=<span class="string">"18"</span>/&gt;</div></pre></td></tr></table></figure>
<h1 id="2-使用相机-intent"><a href="#2-使用相机-intent" class="headerlink" title="2. 使用相机 intent"></a>2. 使用相机 intent</h1><p>实现拍照功能只需要使用一个隐式 intent，分为下面几步：</p>
<ul>
<li>获取保存图片的文件存储位置</li>
<li>处理拍照按钮，实现触发拍照，其实就是发送一个带有 <code>MediaStore.ACTION_IMAGE_CAPTURE</code>的 intent 即可。</li>
</ul>
<p>对于 intent 的操作，我们需要定义在 MediaStore 类中的<code>ACTION_CAPTURE_IMAGE</code>。MediaStore 类定义了一些公共接口，可用于处理图像、视频以及音乐这些常见的多媒体任务。当然，这也包括触发相机应用的拍照 intent。</p>
<p>如果只用<code>ACTION_IMAGE_CAPTURE</code>打开相机应用，默认只能拍摄缩略图这样的低分辨率照片，而且照片会保存在 onActivityResult(…)返回的 Intent 对象里。要想获得全尺寸照片，就要让它使用文件系统存储照片。这可以通过传入保存在 <code>MediaStore.EXTRA_OUTPUT</code> 中的指向存储路径的 Uri 来完成。<br>  编写用于拍照的隐式 intent，拍摄的照片应该保存在 mPhotoFile 指定的地方。同时，别忘了检查设备上是否安装有相机应用，以及是否有地方存储照片。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera);</div><div class="line"><span class="comment">// 首先创建一个用于拍照的 Intent 对象</span></div><div class="line"><span class="keyword">final</span> Intent captureImage = <span class="keyword">new</span> Intent(MediaStore.ACTION_IMAGE_CAPTURE);</div><div class="line"><span class="comment">// 检查是否有可拍照的应用</span></div><div class="line"><span class="keyword">boolean</span> canTakePhoto = mPhotoFile != <span class="keyword">null</span> &amp;&amp;</div><div class="line">        captureImage.resolveActivity(packageManager) != <span class="keyword">null</span>;</div><div class="line">mPhotoButton.setEnabled(canTakePhoto);</div><div class="line"></div><div class="line"><span class="keyword">if</span> (canTakePhoto) &#123;</div><div class="line">	<span class="comment">// 建立访问照片目录的 Uri</span></div><div class="line">    Uri uri = Uri.fromFile(mPhotoFile);</div><div class="line">    <span class="comment">// 将该 Uri 放入 intent 对象中</span></div><div class="line">    captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri);</div><div class="line">&#125;</div><div class="line"></div><div class="line">mPhotoButton.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View view)</span> </span>&#123;</div><div class="line">    	<span class="comment">// 使用 startActivityForResult 是为了拍完照后刷新视图</span></div><div class="line">        startActivityForResult(captureImage, REQUEST_PHOTO);</div><div class="line">    &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<h1 id="3-缩放和显示位图"><a href="#3-缩放和显示位图" class="headerlink" title="3. 缩放和显示位图"></a>3. 缩放和显示位图</h1><p>有了照片，接下来就是找到并加载它，然后展示给用户看。在技术实现上，这需要加载照片到大小合适的 Bitmap 对象中。而要从文件生成 Bitmap 对象，我们需要 BitmapFactory 类:<br>    <code>Bitmap bitmap = BitmapFactory.decodeFile(mPhotoFile.getPath());</code></p>
<p>Bitmap 是个简单对象，它只存储实际像素数据。也就是说，即使原始照片已压缩过，但存入 Bitmap 对象时，文件并不会同样压缩。因此，如果有一个16万像素24位已压缩为5Mb 大小的 JPG 照片文件，一旦载入 Bitmap 对象，就会立即膨胀至48Mb 大小!<br>这个问题可以设法解决，但需要手工缩放位图照片。具体做法就是，首先确认文件到底有多大，然后考虑按照给定区域大小合理缩放文件。最后，重新读取缩放后的文件，创建 Bitmap 对象。<br>既然需要处理图像文件，我们建立一个通用的工具类，名为 PictureUtils.java。在其中添加 getScaledBitmap(String, int, int)缩放方法，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PictureUtils</span> </span>&#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">getScaledBitmap</span><span class="params">(String path, <span class="keyword">int</span> destWidth, <span class="keyword">int</span> destHeight)</span> </span>&#123;</div><div class="line">        <span class="comment">// Read in the dimensions of the image on disk</span></div><div class="line">        BitmapFactory.Options options = <span class="keyword">new</span> BitmapFactory.Options();</div><div class="line">        options.inJustDecodeBounds = <span class="keyword">true</span>;</div><div class="line">        BitmapFactory.decodeFile(path, options);</div><div class="line"></div><div class="line">        <span class="keyword">float</span> srcWidth = options.outWidth;</div><div class="line">        <span class="keyword">float</span> srcHeight = options.outHeight;</div><div class="line"></div><div class="line">        <span class="comment">// Figure out how much to scale down by</span></div><div class="line">        <span class="keyword">int</span> inSampleSize = <span class="number">1</span>;</div><div class="line">        <span class="keyword">if</span> (srcHeight &gt; destHeight || srcWidth &gt; destWidth) &#123;</div><div class="line">            <span class="keyword">if</span> (srcWidth &gt; srcHeight) &#123;</div><div class="line">                inSampleSize = Math.round(srcHeight / destHeight);</div><div class="line">            &#125; <span class="keyword">else</span> &#123;</div><div class="line">                inSampleSize = Math.round(srcWidth / destWidth);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        options = <span class="keyword">new</span> BitmapFactory.Options();</div><div class="line">        options.inSampleSize = inSampleSize;</div><div class="line"></div><div class="line">        <span class="comment">// Read in and create final bitmap</span></div><div class="line">        <span class="keyword">return</span> BitmapFactory.decodeFile(path, options);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>上述方法中，inSampleSize 值很关键。它决定着缩略图像素的大小。假设这个值是1的话，就表明缩略图和原始照片的水平像素大小一样。如果是2的话，它们的水平像素比就是1∶2。因此，inSampleSize 值为2时，缩略图的像素数就是原始文件的四分之一。<br>问题总是接踵而来。解决了缩放问题，又冒出了新问题:fragment 刚启动时，PhotoView 究竟有多大无人知道。onCreate(…)、onStart()和 onResume()方法启动后，才会有首个实例化布局出现。也就在此时，显示在屏幕上的视图才会有大小尺寸。这也是出现新问题的原因。<br>解决方案有两个:要么等布局实例化完成并显示，要么干脆使用保守估算值。特定条件下， 尽管估算比较主观，但确实是一个切实可行的办法。再添加一个 getScaledBitmap(String, Activity)静态 Bitmap 估算方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">getScaledBitmap</span><span class="params">(String path, Activity activity)</span> </span>&#123;</div><div class="line">    Point size = <span class="keyword">new</span> Point();</div><div class="line">    activity.getWindowManager().getDefaultDisplay()</div><div class="line">            .getSize(size);</div><div class="line"></div><div class="line">    <span class="keyword">return</span> getScaledBitmap(path, size.x, size.y);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="4-功能声明"><a href="#4-功能声明" class="headerlink" title="4. 功能声明"></a>4. 功能声明</h1><p>应用的拍照功能用起来不错，但还有件事情要做:告诉目标用户应用具有拍照功能。</p>
<p>假如应用要用到诸如相机、NFC，或者任何其他的随设备走的功能时，都应该要让 Android 系统知道。否则，假如设备缺少这样的功能，类似 Google Play 商店的安装程序就会拒绝安装应用。<br>为声明需要使用相机，在 AndroidManifest.xml 中加入<code>&lt;uses-feature&gt;</code>标签：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">uses-feature</span></span></div><div class="line">    <span class="attr">android:name</span>=<span class="string">"android.hardware.camera2"</span></div><div class="line">    <span class="attr">android:required</span>=<span class="string">"false"</span>/&gt;</div></pre></td></tr></table></figure>
<h1 id="5-布局文件中的-lt-include-gt-标签"><a href="#5-布局文件中的-lt-include-gt-标签" class="headerlink" title="5. 布局文件中的 &lt;include&gt; 标签"></a>5. 布局文件中的 <code>&lt;include&gt;</code> 标签</h1><p>如果有重复的布局可以使用，那么可以采用 include 标签，直接在不同的 layout 中引用。<br>然而，经验表明，布局文件的优点是可靠又好用。例如，直接查看布局文件内容，就可以快速准确地知道应用视图是如何构建的。然而，一旦用了 include 标签，一切就不好说了。还想明白视图构成的话，就得仔细翻看布局主文件以及所有 include 的布局文件。这种非直观的感觉，极易让人失去耐心。<br>用户界面是应用改动相对频繁的部分。既然这样，不顾一切地追求复用原则很可能会适得其反。因此，在视图层开发时，我们一定要多多考量，尽量做到审慎、合理地使用 include 标签。</p>
<h1 id="6-挑战练习"><a href="#6-挑战练习" class="headerlink" title="6. 挑战练习"></a>6. 挑战练习</h1><h2 id="6-1-优化照片显示"><a href="#6-1-优化照片显示" class="headerlink" title="6.1 优化照片显示"></a>6.1 优化照片显示</h2><p>新建一个 GlancePictureFragment，继承自 DialogFragment，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GlancePictureFragment</span> <span class="keyword">extends</span> <span class="title">DialogFragment</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String ARG_PATH = <span class="string">"path"</span>;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> ImageView mImage;</div><div class="line"></div><div class="line">	<span class="comment">// 由于文件比较大，所以将文件路径传入即可</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> GlancePictureFragment <span class="title">newInstance</span><span class="params">(String path)</span> </span>&#123;</div><div class="line">        Bundle args = <span class="keyword">new</span> Bundle();</div><div class="line">        args.putString(ARG_PATH, path);</div><div class="line">        GlancePictureFragment fragment = <span class="keyword">new</span> GlancePictureFragment();</div><div class="line">        fragment.setArguments(args);</div><div class="line">        <span class="keyword">return</span> fragment;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@NonNull</span></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Dialog <span class="title">onCreateDialog</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">		<span class="comment">// 使用 getArguments() 方法取出照片文件路径</span></div><div class="line">        String path = getArguments().getString(ARG_PATH);</div><div class="line"></div><div class="line">		<span class="comment">// 这个新的 style 其实就做了一件事，那就是使窗口全屏</span></div><div class="line">		<span class="comment">// 注意如果继承了 @android:Theme.Dialog 的话，窗口</span></div><div class="line">		<span class="comment">// 大小就限定了，所以我没有继承</span></div><div class="line">        <span class="keyword">final</span> Dialog dialog = <span class="keyword">new</span> Dialog(getActivity(), R.style.CustomDialogTheme);</div><div class="line">        <span class="comment">// 这个 layout 中只有一个 ImageView</span></div><div class="line">        dialog.setContentView(R.layout.dialog_image_glance);</div><div class="line"></div><div class="line">        mImage = (ImageView) dialog.findViewById(R.id.glance_image);</div><div class="line">        <span class="comment">// 仍然使用 PictureUtils 类的工具来获得缩放的 Bitmap</span></div><div class="line">        mImage.setImageBitmap(</div><div class="line">                PictureUtils.getScaledBitmap(path, getActivity()));</div><div class="line">        mImage.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</div><div class="line">            <span class="meta">@Override</span></div><div class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>&#123;</div><div class="line">            <span class="comment">// 点击图片则退出该 dialog</span></div><div class="line">                dialog.dismiss();</div><div class="line">            &#125;</div><div class="line">        &#125;);</div><div class="line">        <span class="keyword">return</span> dialog;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后在图片的点击事件中声明即可</p>
<h2 id="6-2-优化缩略图加载"><a href="#6-2-优化缩略图加载" class="headerlink" title="6.2 优化缩略图加载"></a>6.2 优化缩略图加载</h2><p>首先修改更新视图的函数，接受高宽的指定像素：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">updatePhotoView</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</div><div class="line">    <span class="keyword">if</span> (mPhotoFile == <span class="keyword">null</span> || !mPhotoFile.exists()) &#123;</div><div class="line">        mPhotoView.setImageDrawable(<span class="keyword">null</span>);</div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">        Bitmap bitmap = PictureUtils.getScaledBitmap(</div><div class="line">                mPhotoFile.getPath(), width, height);</div><div class="line">        mPhotoView.setImageBitmap(bitmap);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>之后，先获取 mPhotoView 的 ViewTreeObserver，然后设置 OnGlobalLayoutListener 监听器，在监听器中即可获取视图的高度和宽度，然后进行图片显示。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">mPhotoObserver = mPhotoView.getViewTreeObserver();</div><div class="line">mPhotoObserver.addOnGlobalLayoutListener(</div><div class="line">        <span class="keyword">new</span> ViewTreeObserver.OnGlobalLayoutListener() &#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onGlobalLayout</span><span class="params">()</span> </span>&#123;</div><div class="line">        updatePhotoView(</div><div class="line">                mPhotoView.getWidth(),</div><div class="line">                mPhotoView.getHeight());</div><div class="line">        Log.i(<span class="string">"CrimeFragment"</span>, <span class="string">"onGlobalLayout: Observed"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Android/" rel="tag"># Android</a>
          
        </div>
      

      
        
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2017-01-11-cj0ag5y67000uifs62u85ujxv/" rel="next" title="Android编程权威指南（第二版）学习笔记（十五）—— 第15章 隐式 Intent">
                <i class="fa fa-chevron-left"></i> Android编程权威指南（第二版）学习笔记（十五）—— 第15章 隐式 Intent
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2017-01-15-cj0ag5y6b0011ifs66mlphdl8/" rel="prev" title="Android编程权威指南（第二版）学习笔记（十七）—— 第17章 Master-Detail 用户界面">
                Android编程权威指南（第二版）学习笔记（十七）—— 第17章 Master-Detail 用户界面 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>

          
          </div>
          


          
  <div class="comments" id="comments">
    
      <div id="disqus_thread"></div>
<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL;  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://kniost-comment-github.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                                
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/images/avatar.jpg"
               alt="kniost" />
          <p class="site-author-name" itemprop="name">kniost</p>
           
              <p class="site-description motion-element" itemprop="description">kniost 的成长与疑惑</p>
          
        </div>
        <nav class="site-state motion-element">

          
            <div class="site-state-item site-state-posts">
              <a href="/archives">
                <span class="site-state-item-count">31</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-categories">
              <a href="/categories/index.html">
                <span class="site-state-item-count">3</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-tags">
              <a href="/tags/index.html">
                <span class="site-state-item-count">3</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        
          <div class="feed-link motion-element">
            <a href="/atom.xml" rel="alternate">
              <i class="fa fa-rss"></i>
              RSS
            </a>
          </div>
        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-外部存储"><span class="nav-text">1. 外部存储</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-1-指定照片存放位置"><span class="nav-text">1.1 指定照片存放位置</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-2-外部存储使用权限"><span class="nav-text">1.2 外部存储使用权限</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-使用相机-intent"><span class="nav-text">2. 使用相机 intent</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#3-缩放和显示位图"><span class="nav-text">3. 缩放和显示位图</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#4-功能声明"><span class="nav-text">4. 功能声明</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#5-布局文件中的-lt-include-gt-标签"><span class="nav-text">5. 布局文件中的 <include> 标签</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#6-挑战练习"><span class="nav-text">6. 挑战练习</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#6-1-优化照片显示"><span class="nav-text">6.1 优化照片显示</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-2-优化缩略图加载"><span class="nav-text">6.2 优化缩略图加载</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2017</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">kniost</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Pisces
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    
    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  




  
  <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script>



  



  

    <script type="text/javascript">
      var disqus_shortname = 'kniost-comment-github';
      var disqus_identifier = '2017-01-12-cj0ag5y69000yifs6oc1j0hco/';

      var disqus_title = "Android编程权威指南（第二版）学习笔记（十六）—— 第16章 使用 intent 拍照";


      function run_disqus_script(disqus_script) {
        var dsq = document.createElement('script');
        dsq.type = 'text/javascript';
        dsq.async = true;
        dsq.src = '//' + disqus_shortname + '.disqus.com/' + disqus_script;
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
      }

      run_disqus_script('count.js');

      
        var disqus_config = function () {
            this.page.url = disqus_url;
            this.page.identifier = disqus_identifier;
            this.page.title = disqus_title;
        };
        run_disqus_script('embed.js');
      

    </script>
  










  
  

  

  

  

  


  

</body>
</html>
